refactor(card): rework Card component architecture and API#1359
refactor(card): rework Card component architecture and API#1359
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
@claude do a thorough review of this branches changes |
a758562 to
6c27993
Compare
Proposes removing the context-registration anti-pattern, renaming Content to Body, adding Footer, and aligning variant props with Nimbus conventions (variant + size).
…d standard variant/size API Remove the context-based child registration pattern (useState + useEffect) that caused re-render risks, replacing it with direct rendering matching all other Nimbus compound components. Rename Card.Content to Card.Body for naming consistency, add Card.Footer sub-component, and replace the four visual props (cardPadding, borderStyle, elevation, backgroundStyle) with standard Nimbus variant/size props. Padding is now CSS-only via :first-child/:last-child pseudo-selectors.
Rewrite stories to use Card.Body, Card.Footer, variant, and size props with play functions for all combinations. Update docs spec with footer and variant/size test cases. Update MDX documentation (dev, a11y, guidelines) to replace all old prop references.
…adding Extract repeated slot padding rules into a shared --card-padding CSS variable set on root per size, eliminating 3x duplication across header/body/footer slots.
The variable drives spacing for both padding and gap, so the name should not imply padding only.
…ers into HeaderBodyFooter story Move gap to base (derives from --card-spacing like slot padding), so size variants only set the single CSS variable. Merge the WithDividers story into HeaderBodyFooter as a side-by-side comparison across all sizes.
…alette.6 for border Merge HeaderBodyFooter, BodyOnly, HeaderAndBody, and WithDividers into a single PartCombinations story showing all realistic part permutations across sizes with and without dividers. Change outlined border from colorPalette.3 to colorPalette.6 to match the Separator color.
spacing.200 (8px) felt too condensed for the sm size. Bump to spacing.300 (12px) for a more comfortable small variant. Final scale: sm=12px, md=16px, lg=24px — matching Mantine's card sizing.
Slots now own their spacing via full padding instead of root gap. Adjacent sibling selectors collapse doubled padding when slots sit next to each other; separators break adjacency so both sides keep full padding naturally.
Card.Root now uses React Aria's useSlotId + Provider pattern to automatically wire aria-labelledby and aria-describedby when consumers place <Heading slot="title"> or <Text slot="description"> inside the card. The card conditionally gains role="article" only when at least one slot is detected — cards without slots remain plain divs. Follows the Adobe React Spectrum Card spec (adobe/react-spectrum#2080). Uses the slots map format with DEFAULT_SLOT for strict slot matching, ensuring plain <Heading> and <Text> components are unaffected.
…mentation - Update spec and proposal to reflect actual values: colorPalette.6 for outlined border, spacing.300 for sm size, unified --card-spacing model instead of separate gap tokens - Document adjacent sibling selector strategy (not :first-child/:last-child) in spec and proposal, with explanation of intentional Separator spacing - Remove unnecessary `as any` cast from Provider values in card.root.tsx - Add comments to recipe explaining adjacent-sibling padding collapsing - Mark tasks 13–18 as complete in tasks.md
Update frontpage and category-overview to use the reworked Card API: - Card.Content → Card.Body - borderStyle prop → variant prop - cardPadding prop → size prop - Make frontpage nav cards responsive (1 col mobile, 2 col desktop)
Replace Card.Content with Card.Body and replace borderStyle/elevation props with the new variant prop after the Card component rework.
6c27993 to
47c8ad1
Compare
|
@claude please review |
|
Claude finished @misama-ct's task in 3m 59s —— View job Review:
|
Note
Card was one of the early components and we made a couple of questionable decisions. This PR addresses them. There are breaking changes, but fortunately no consumers yet.
What changed
Architecture: direct rendering replaces context registration
Card.Header and Card.Content previously returned
nulland useduseEffectto register JSX into parent state via context callbacks. This pattern — unique among all Nimbus compound components — introduced re-render risk, stale closures, and a double render cycle.Now Header, Body, and Footer are simple passthrough components that render their slot directly, matching Dialog.Body, Dialog.Footer, and every other Nimbus compound component.
Naming: Content → Body, Footer added
Card.Contentrenamed toCard.Body(aligns with Dialog.Body, Drawer.Body convention)Card.Footeradded (matches Dialog, DefaultPage, and every major UI library)Props: four visual props → standard
variant+sizecardPadding,borderStyle,elevation, andbackgroundStyleare replaced by the standard Nimbusvariantandsizeprops.size(sm | md | lg, default: md) — sets--card-spacingCSS variable:--card-spacingspacing.300spacing.400spacing.600variant(outlined | elevated | filled | plain, default: outlined):Spacing: CSS-only padding distribution via
--card-spacingA unified
--card-spacingCSS variable controls all slot padding. Each slot getsp: var(--card-spacing). When two card slots are directly adjacent, the later slot suppresses its top padding via adjacent sibling selectors. When a non-slot element (e.g. Separator) sits between slots, both retain full padding for visually balanced spacing.Accessibility: slot-based ARIA wiring
Card.Root uses React Aria's
useSlotId+Providerpattern to automatically wirearia-labelledbyandaria-describedbywhen<Heading slot="title">or<Text slot="description">are present. Cards with slots getrole="article"; cards without remain plain<div>s.Layout:
inline-flex→flexCards are now block-level containers that fill available width by default.
Consumer API
Files
openspec/changes/rework-card-component/— proposal, spec delta, and task listpackages/nimbus/src/components/card/— full implementation, stories, docs spec, MDX docsTest plan
pnpm --filter @commercetools/nimbus typecheckpassespnpm lintpassespnpm test:dev packages/nimbus/src/components/card/card.stories.tsx— all stories passpnpm test:dev packages/nimbus/src/components/card/card.docs.spec.tsx— all doc specs pass